home *** CD-ROM | disk | FTP | other *** search
/ Team Palmtops 7 / Palmtops_numero07.iso / WinCE / SDKWindowsCE / HandHeldPCPro30 / sdk.exe / Jupiter SDK / data1.cab / MFC / src / dlgprop.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1999-02-19  |  43.0 KB  |  1,628 lines

  1. // This is a part of the Microsoft Foundation Classes C++ library.
  2. // Copyright (C) 1992-1998 Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // This source code is only intended as a supplement to the
  6. // Microsoft Foundation Classes Reference and related
  7. // electronic documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Microsoft Foundation Classes product.
  10.  
  11. #include "stdafx.h"
  12. #include "occimpl.h"
  13.  
  14. #ifdef AFX_CORE4_SEG
  15. #pragma code_seg(AFX_CORE4_SEG)
  16. #endif
  17.  
  18. #ifdef _DEBUG
  19. #undef THIS_FILE
  20. static char THIS_FILE[] = __FILE__;
  21. #endif
  22.  
  23. #define new DEBUG_NEW
  24.  
  25. ////////////////////////////////////////////////////////////////////////////
  26. // CPropertyPage -- one page of a tabbed dialog
  27.  
  28. UINT CALLBACK
  29. AfxPropPageCallback(HWND, UINT message, LPPROPSHEETPAGE pPropPage)
  30. {
  31.     switch (message)
  32.     {
  33.     case PSPCB_CREATE:
  34.         {
  35.             ASSERT(AfxIsValidAddress(pPropPage, sizeof(AFX_OLDPROPSHEETPAGE)));
  36.             ASSERT(AfxIsValidAddress(pPropPage, pPropPage->dwSize));
  37.             CPropertyPage* pPage =
  38.                 STATIC_DOWNCAST(CPropertyPage, (CObject*)pPropPage->lParam);
  39.             ASSERT_VALID(pPage);
  40.             TRY
  41.             {
  42.                 AfxHookWindowCreate(pPage);
  43.             }
  44.             CATCH_ALL(e)
  45.             {
  46.                 // Note: DELETE_EXCEPTION(e) not necessary
  47.                 return FALSE;
  48.             }
  49.             END_CATCH_ALL
  50.         }
  51.         return TRUE;
  52.  
  53.     case PSPCB_RELEASE:
  54.         AfxUnhookWindowCreate();
  55.         break;
  56.     }
  57.  
  58.     return 0;
  59. }
  60.  
  61. BEGIN_MESSAGE_MAP(CPropertyPage, CDialog)
  62.     //{{AFX_MSG_MAP(CPropertyPage)
  63.     ON_WM_CTLCOLOR()
  64.     //}}AFX_MSG_MAP
  65. #ifndef _AFX_NO_CTL3D_SUPPORT
  66.     ON_MESSAGE(WM_QUERY3DCONTROLS, OnQuery3dControls)
  67. #endif
  68. END_MESSAGE_MAP()
  69.  
  70. CPropertyPage::CPropertyPage(UINT nIDTemplate, UINT nIDCaption)
  71. {
  72.     ASSERT(nIDTemplate != 0);
  73.     CommonConstruct(MAKEINTRESOURCE(nIDTemplate), nIDCaption);
  74. }
  75.  
  76. CPropertyPage::CPropertyPage(LPCTSTR lpszTemplateName, UINT nIDCaption)
  77. {
  78.     ASSERT(AfxIsValidString(lpszTemplateName));
  79.     CommonConstruct(lpszTemplateName, nIDCaption);
  80. }
  81.  
  82. void CPropertyPage::Construct(UINT nIDTemplate, UINT nIDCaption)
  83. {
  84.     ASSERT(nIDTemplate != 0);
  85.     CommonConstruct(MAKEINTRESOURCE(nIDTemplate), nIDCaption);
  86. }
  87.  
  88. void CPropertyPage::Construct(LPCTSTR lpszTemplateName, UINT nIDCaption)
  89. {
  90.     ASSERT(HIWORD(lpszTemplateName) == 0 ||
  91.         AfxIsValidString(lpszTemplateName));
  92.     CommonConstruct(lpszTemplateName, nIDCaption);
  93. }
  94.  
  95. CPropertyPage::CPropertyPage()
  96. {
  97.     CommonConstruct(NULL, 0);
  98. }
  99.  
  100. void CPropertyPage::CommonConstruct(LPCTSTR lpszTemplateName, UINT nIDCaption)
  101. {
  102.     memset(&m_psp, 0, sizeof(m_psp));
  103.     m_psp.dwSize = sizeof(m_psp);
  104.     m_psp.dwFlags = PSP_USECALLBACK;
  105.     if (lpszTemplateName != NULL)
  106.         m_psp.hInstance = AfxFindResourceHandle(lpszTemplateName, RT_DIALOG);
  107.     m_psp.pszTemplate = lpszTemplateName;
  108.     m_psp.pfnDlgProc = WCE_IF(wce_FirstDlgProc, AfxDlgProc);
  109.     m_psp.lParam = (LPARAM)this;
  110.     m_psp.pfnCallback = AfxPropPageCallback;
  111.     if (nIDCaption != 0)
  112.     {
  113.         VERIFY(m_strCaption.LoadString(nIDCaption));
  114.         m_psp.pszTitle = m_strCaption;
  115.         m_psp.dwFlags |= PSP_USETITLE;
  116.     }
  117.     if (AfxHelpEnabled())
  118.         m_psp.dwFlags |= PSP_HASHELP;
  119.     if (HIWORD(lpszTemplateName) == 0)
  120.         m_nIDHelp = LOWORD((DWORD)lpszTemplateName);
  121.     m_lpszTemplateName = m_psp.pszTemplate;
  122.     m_bFirstSetActive = TRUE;
  123. }
  124.  
  125. CPropertyPage::~CPropertyPage()
  126. {
  127. #ifndef _AFX_NO_OCC_SUPPORT
  128.     Cleanup();
  129. #endif
  130.  
  131.     if (m_hDialogTemplate != NULL)
  132.         WCE_FCTN(GlobalFree)(m_hDialogTemplate);
  133. }
  134.  
  135. #ifndef _AFX_NO_OCC_SUPPORT
  136.  
  137. void CPropertyPage::Cleanup()
  138. {
  139.     COccManager* pOccManager = afxOccManager;
  140.     if ((pOccManager != NULL) && (m_pOccDialogInfo != NULL))
  141.     {
  142.         pOccManager->PostCreateDialog(m_pOccDialogInfo);
  143.         free(m_pOccDialogInfo);
  144.         m_pOccDialogInfo = NULL;
  145.     }
  146. }
  147.  
  148. AFX_STATIC DLGTEMPLATE* AFXAPI
  149. _AfxChangePropPageFont(const DLGTEMPLATE* pTemplate, BOOL bWizard)
  150. {
  151.     CString strFaceDefault;
  152.     WORD wSizeDefault;
  153.  
  154.     if (!AfxGetPropSheetFont(strFaceDefault, wSizeDefault, bWizard))
  155.         return NULL;
  156.  
  157.     // set font of property page to same font used by property sheet
  158.     CString strFace;
  159.     WORD wSize;
  160.     if ((!CDialogTemplate::GetFont(pTemplate, strFace, wSize)) ||
  161.         (strFace != strFaceDefault) || (wSize != wSizeDefault))
  162.     {
  163.         CDialogTemplate dlgTemplate(pTemplate);
  164.         dlgTemplate.SetFont(strFaceDefault, wSizeDefault);
  165.         return (DLGTEMPLATE*)dlgTemplate.Detach();
  166.     }
  167.  
  168.     return NULL;
  169. }
  170.  
  171. const DLGTEMPLATE* CPropertyPage::InitDialogInfo(const DLGTEMPLATE* pTemplate)
  172. {
  173.     // cleanup from previous run, if any
  174.     Cleanup();
  175.  
  176.     m_pOccDialogInfo = (_AFX_OCC_DIALOG_INFO*)malloc(
  177.         sizeof(_AFX_OCC_DIALOG_INFO));
  178.  
  179.     return afxOccManager->PreCreateDialog(m_pOccDialogInfo, pTemplate);
  180. }
  181.  
  182. #endif
  183.  
  184. #if !defined(_WIN32_WCE)
  185. void CPropertyPage::PreProcessPageTemplate(PROPSHEETPAGE& psp, BOOL bWizard)
  186. {
  187.     const DLGTEMPLATE* pTemplate;
  188.  
  189.     if (psp.dwFlags & PSP_DLGINDIRECT)
  190.     {
  191.         pTemplate = psp.pResource;
  192.     }
  193.     else
  194.     {
  195.         HRSRC hResource = ::FindResource(psp.hInstance,
  196.             psp.pszTemplate, RT_DIALOG);
  197.         HGLOBAL hTemplate = LoadResource(psp.hInstance,
  198.             hResource);
  199.         pTemplate = (LPCDLGTEMPLATE)LockResource(hTemplate);
  200.     }
  201.  
  202.     ASSERT(pTemplate != NULL);
  203.  
  204. #ifdef _DEBUG
  205.     // WINBUG: Windows currently does not support DIALOGEX resources!
  206.     // Assert that the template is *not* a DIALOGEX template.
  207.     // DIALOGEX templates are not supported by the PropertySheet API.
  208.  
  209.     // To change a DIALOGEX template back to a DIALOG template,
  210.     // remove the following:
  211.     //  1. Extended styles on the dialog
  212.     //  2. Help IDs on any control in the dialog
  213.     //  3. Control IDs that are DWORDs
  214.     //  4. Weight, italic, or charset attributes on the dialog's font
  215.  
  216.     if (((DLGTEMPLATEEX*)pTemplate)->signature == 0xFFFF)
  217.     {
  218.         // it's a DIALOGEX -- we'd better check
  219.         DWORD dwVersion = ::GetVersion();
  220.         if (dwVersion & 0x80000000)
  221.         {
  222.             // it's Win95 -- versions of COMCTL32.DLL that export
  223.             // a function called DllGetVersion are okay
  224.  
  225.             HINSTANCE hInst = LoadLibrary(WCE_IF(_T("COMMCTRL.DLL"),_T("COMCTL32.DLL")));
  226.             ASSERT(hInst != NULL);
  227.             if (hInst != NULL)
  228.             {
  229.                 FARPROC proc = GetProcAddress(hInst, "DllGetVersion");
  230.                 if (proc == NULL)
  231.                     ASSERT(FALSE);
  232.                 FreeLibrary(hInst);
  233.             }
  234.         }
  235.         else if (LOBYTE(LOWORD(dwVersion)) == 3)
  236.         {
  237.             // it's Windows NT 3.x; we have no hope of this working
  238.             ASSERT(FALSE);
  239.         }
  240.     }
  241. #endif // _DEBUG
  242.  
  243. #ifndef _AFX_NO_OCC_SUPPORT
  244.     // if the dialog could contain OLE controls, deal with them now
  245.     if (afxOccManager != NULL)
  246.         pTemplate = InitDialogInfo(pTemplate);
  247. #endif
  248.  
  249.     // set font of property page to same font used by property sheet
  250.     HGLOBAL hTemplate = _AfxChangePropPageFont(pTemplate, bWizard);
  251.  
  252.     if (m_hDialogTemplate != NULL)
  253.     {
  254.         WCE_FCTN(GlobalFree)(m_hDialogTemplate);
  255.         m_hDialogTemplate = NULL;
  256.     }
  257.  
  258.     if (hTemplate != NULL)
  259.     {
  260.         pTemplate = (LPCDLGTEMPLATE)hTemplate;
  261.         m_hDialogTemplate = hTemplate;
  262.     }
  263.     psp.pResource = pTemplate;
  264.     psp.dwFlags |= PSP_DLGINDIRECT;
  265. }
  266. #endif // _WIN32_WCE
  267.  
  268. void CPropertyPage::CancelToClose()
  269. {
  270.     ASSERT(::IsWindow(m_hWnd));
  271.     ASSERT(GetParent() != NULL);
  272.  
  273.     GetParent()->SendMessage(PSM_CANCELTOCLOSE);
  274. }
  275.  
  276. void CPropertyPage::SetModified(BOOL bChanged)
  277. {
  278.     if (m_hWnd == NULL) // allowed for backward compatibility
  279.         return;
  280.  
  281.     ASSERT(::IsWindow(m_hWnd));
  282.     ASSERT(GetParent() != NULL);
  283.  
  284.     CWnd* pParentWnd = GetParent();
  285.     if (bChanged)
  286.         pParentWnd->SendMessage(PSM_CHANGED, (WPARAM)m_hWnd);
  287.     else
  288.         pParentWnd->SendMessage(PSM_UNCHANGED, (WPARAM)m_hWnd);
  289. }
  290.  
  291. LRESULT CPropertyPage::QuerySiblings(WPARAM wParam, LPARAM lParam)
  292. {
  293.     ASSERT(::IsWindow(m_hWnd));
  294.     ASSERT(GetParent() != NULL);
  295.  
  296.     return GetParent()->SendMessage(PSM_QUERYSIBLINGS, wParam, lParam);
  297. }
  298.  
  299. BOOL CPropertyPage::OnApply()
  300. {
  301.     ASSERT_VALID(this);
  302.  
  303.     OnOK();
  304.     return TRUE;
  305. }
  306.  
  307. void CPropertyPage::OnReset()
  308. {
  309.     ASSERT_VALID(this);
  310.  
  311.     OnCancel();
  312. }
  313.  
  314. void CPropertyPage::OnOK()
  315. {
  316.     ASSERT_VALID(this);
  317. }
  318.  
  319. void CPropertyPage::OnCancel()
  320. {
  321.     ASSERT_VALID(this);
  322. }
  323.  
  324. BOOL CPropertyPage::OnSetActive()
  325. {
  326.     ASSERT_VALID(this);
  327.  
  328.     if (m_bFirstSetActive)
  329.         m_bFirstSetActive = FALSE;
  330.     else
  331.         UpdateData(FALSE);
  332.     return TRUE;
  333. }
  334.  
  335. BOOL CPropertyPage::OnKillActive()
  336. {
  337.     ASSERT_VALID(this);
  338.  
  339.     if (!UpdateData())
  340.     {
  341.         TRACE0("UpdateData failed during page deactivation\n");
  342.         return FALSE;
  343.     }
  344.     return TRUE;
  345. }
  346.  
  347. BOOL CPropertyPage::OnQueryCancel()
  348. {
  349.     return TRUE;    // ok to cancel
  350. }
  351.  
  352. #if !defined(_WIN32_WCE)
  353. LRESULT CPropertyPage::OnWizardBack()
  354. {
  355.     return 0;
  356. }
  357.  
  358. LRESULT CPropertyPage::OnWizardNext()
  359. {
  360.     return 0;
  361. }
  362.  
  363. BOOL CPropertyPage::OnWizardFinish()
  364. {
  365.     return TRUE;
  366. }
  367.  
  368. LRESULT CPropertyPage::MapWizardResult(LRESULT lToMap)
  369. {
  370.     // -1 and 0 are special
  371.     if (lToMap == -1 || lToMap == 0)
  372.         return lToMap;
  373.  
  374.     // only do special stuff if MFC owns the property sheet
  375.     CWnd* pParent = GetParent();
  376.     CPropertySheet* pSheet = DYNAMIC_DOWNCAST(CPropertySheet, pParent);
  377.     if (pSheet != NULL)
  378.     {
  379.         // the structures are laid out different for CPropertySheeEx
  380.         CPropertySheetEx* pSheetEx = DYNAMIC_DOWNCAST(CPropertySheetEx, pParent);
  381.         const PROPSHEETPAGE* ppsp;
  382.         if (pSheetEx != NULL)
  383.             ppsp = pSheetEx->m_psh.ppsp;
  384.         else
  385.             ppsp = pSheet->m_psh.ppsp;
  386.  
  387.         // search the pages for a matching ID
  388.         for (int i = 0; i < pSheet->m_pages.GetSize(); i++)
  389.         {
  390.             // check page[i] for a match
  391.             CPropertyPage* pPage = pSheet->GetPage(i);
  392.             if ((LRESULT)pPage->m_psp.pszTemplate == lToMap)
  393.                 return (LRESULT)ppsp->pResource;
  394.  
  395.             // jump to next page
  396.             (BYTE*&)ppsp += ppsp->dwSize;
  397.         }
  398.     }
  399.     // otherwise, just use the original value
  400.     return lToMap;
  401. }
  402. #endif // _WIN32_WCE
  403.  
  404. BOOL CPropertyPage::IsButtonEnabled(int iButton)
  405. {
  406.     HWND hWnd = ::GetDlgItem(::GetParent(m_hWnd), iButton);
  407.     if (hWnd == NULL)
  408.         return FALSE;
  409.     return ::IsWindowEnabled(hWnd);
  410. }
  411.  
  412. BOOL CPropertyPage::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
  413. {
  414.     ASSERT(pResult != NULL);
  415.     NMHDR* pNMHDR = (NMHDR*)lParam;
  416.  
  417.     // allow message map to override
  418.     if (CDialog::OnNotify(wParam, lParam, pResult))
  419.         return TRUE;
  420.  
  421.     // don't handle messages not from the page/sheet itself
  422.     if (pNMHDR->hwndFrom != m_hWnd && pNMHDR->hwndFrom != ::GetParent(m_hWnd))
  423.         return FALSE;
  424.  
  425.     // handle default
  426.     switch (pNMHDR->code)
  427.     {
  428.     case PSN_SETACTIVE:
  429.         {
  430.             CPropertySheet* pSheet = DYNAMIC_DOWNCAST(CPropertySheet, GetParent());
  431.             if (pSheet != NULL && !(pSheet->m_nFlags & WF_CONTINUEMODAL) && !(pSheet->m_bModeless))
  432.                 *pResult = -1;
  433.             else
  434.                 *pResult = OnSetActive() ? 0 : -1;
  435.         }
  436.         break;
  437.     case PSN_KILLACTIVE:
  438.         *pResult = !OnKillActive();
  439.         break;
  440.     case PSN_APPLY:
  441.         *pResult = OnApply() ? PSNRET_NOERROR : PSNRET_INVALID_NOCHANGEPAGE;
  442.         break;
  443.     case PSN_RESET:
  444.         OnReset();
  445.         break;
  446.     case PSN_QUERYCANCEL:
  447.         *pResult = !OnQueryCancel();
  448.         break;
  449. #if !defined(_WIN32_WCE)
  450.     case PSN_WIZNEXT:
  451.         //WINBUG: Win32 will send a PSN_WIZBACK even if the button is disabled.
  452.         if (IsButtonEnabled(ID_WIZNEXT))
  453.             *pResult = MapWizardResult(OnWizardNext());
  454.         break;
  455.     case PSN_WIZBACK:
  456.         //WINBUG: Win32 will send a PSN_WIZBACK even if the button is disabled.
  457.         if (IsButtonEnabled(ID_WIZBACK))
  458.             *pResult = MapWizardResult(OnWizardBack());
  459.         break;
  460.     case PSN_WIZFINISH:
  461.         *pResult = !OnWizardFinish();
  462.         break;
  463. #endif // _WIN32_WCE
  464.     case PSN_HELP:
  465.         SendMessage(WM_COMMAND, ID_HELP);
  466.         break;
  467.  
  468.     default:
  469.         return FALSE;   // not handled
  470.     }
  471.  
  472.     return TRUE;    // handled
  473. }
  474.  
  475. /////////////////////////////////////////////////////////////////////////////
  476. // CPropertyPage message Handlers
  477.  
  478. BOOL CPropertyPage::PreTranslateMessage(MSG* pMsg)
  479. {
  480.     VERIFY(!CWnd::PreTranslateMessage(pMsg));
  481.  
  482.     return FALSE;   // handled by CPropertySheet::PreTranslateMessage
  483. }
  484.  
  485. HBRUSH CPropertyPage::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
  486. {
  487.     LRESULT lResult;
  488.     if (pWnd->SendChildNotifyLastMsg(&lResult))
  489.         return (HBRUSH)lResult;
  490.  
  491.     if (afxData.bWin4)
  492.         return CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
  493.  
  494.     if (!GrayCtlColor(pDC->m_hDC, pWnd->GetSafeHwnd(), nCtlColor,
  495.       afxData.hbrBtnFace, afxData.clrBtnText))
  496.         return (HBRUSH)Default();
  497.     return afxData.hbrBtnFace;
  498. }
  499.  
  500. /////////////////////////////////////////////////////////////////////////////
  501. // CPropertyPage Diagnostics
  502.  
  503. #ifdef _DEBUG
  504. void CPropertyPage::AssertValid() const
  505. {
  506.     CDialog::AssertValid();
  507.     ASSERT(m_psp.dwSize == sizeof(m_psp));
  508.     ASSERT(m_psp.dwFlags & PSP_USECALLBACK);
  509.     ASSERT(m_psp.pfnDlgProc == WCE_IF(wce_FirstDlgProc, AfxDlgProc));
  510.     ASSERT(m_psp.lParam == (LPARAM)this);
  511. }
  512.  
  513. void CPropertyPage::Dump(CDumpContext& dc) const
  514. {
  515.     CDialog::Dump(dc);
  516.  
  517.     dc << "m_strCaption = " << m_strCaption << "\n";
  518.     dc << "m_psp.dwFlags = " << m_psp.dwFlags << "\n";
  519. }
  520. #endif //_DEBUG
  521.  
  522. void CPropertyPage::EndDialog(int nID)
  523. {
  524.     // Normally you shouldn't call EndDialog from a page. But in case it does
  525.     // happen during error situations, call CPropertySheet::EndDialog instead.
  526.  
  527.     CPropertySheet* pParent = DYNAMIC_DOWNCAST(CPropertySheet, GetParent());
  528.     if (pParent != NULL)
  529.         pParent->EndDialog(nID);
  530. }
  531.  
  532. /////////////////////////////////////////////////////////////////////////////
  533. // CPropertySheet -- a tabbed "dialog" (really a popup-window)
  534.  
  535. BEGIN_MESSAGE_MAP(CPropertySheet, CWnd)
  536.     //{{AFX_MSG_MAP(CPropertySheet)
  537.     ON_WM_CTLCOLOR()
  538. WCE_DEL    ON_WM_NCCREATE()
  539.     ON_MESSAGE(WM_INITDIALOG, HandleInitDialog)
  540.     ON_MESSAGE(WM_COMMANDHELP, OnCommandHelp)
  541.     ON_WM_CLOSE()
  542.     ON_WM_SYSCOMMAND()
  543.     ON_MESSAGE(DM_SETDEFID, OnSetDefID)
  544.     //}}AFX_MSG_MAP
  545. END_MESSAGE_MAP()
  546.  
  547. AFX_STATIC_DATA const int _afxPropSheetIDs[4] = { ID_WIZNEXT, ID_WIZFINISH, ID_WIZBACK, IDCANCEL };
  548.  
  549. AFX_OLDPROPSHEETHEADER* CPropertySheet::GetPropSheetHeader()
  550. {
  551. #if !defined(_WIN32_WCE)
  552.     CPropertySheetEx* pSheetEx = DYNAMIC_DOWNCAST(CPropertySheetEx, this);
  553.     if (pSheetEx != NULL)
  554.         return (AFX_OLDPROPSHEETHEADER*)&pSheetEx->m_psh;
  555.     else
  556. #endif
  557.         return &m_psh;
  558. }
  559.  
  560. LRESULT CPropertySheet::OnSetDefID(WPARAM wParam, LPARAM lParam)
  561. {
  562.     // WINBUG -- A wrong or invalid ID may be passed in here.  If this is the
  563.     // case, then look for a valid one.
  564.     HWND hWnd;
  565.     if (IsWizard() &&
  566.         (
  567.             ((hWnd = ::GetDlgItem(m_hWnd, wParam)) == NULL) ||
  568.             !(::GetWindowLong(hWnd, GWL_STYLE) & WS_VISIBLE) ||
  569.             !::IsWindowEnabled(hWnd)
  570.         ))
  571.     {
  572.  
  573.         for (int i = 0; i < 4; i++)
  574.         {
  575.             // find first button that is visible and  enabled
  576.             HWND hWnd = ::GetDlgItem(m_hWnd, _afxPropSheetIDs[i]);
  577.             if ((GetWindowLong(hWnd, GWL_STYLE) & WS_VISIBLE) &&
  578.                 ::IsWindowEnabled(hWnd))
  579.             {
  580.                 //WINBUG -- focus could be incorrect as well in this case
  581.                 // so ... let's set it to the default button
  582.                 HWND hWndFocus = ::GetFocus();
  583.                 if (!::IsWindowEnabled(hWndFocus))
  584.                     ::SetFocus(hWnd);
  585.                 return DefWindowProc(DM_SETDEFID, _afxPropSheetIDs[i], lParam);
  586.             }
  587.         }
  588.     }
  589.     return Default();
  590. }
  591.  
  592. CPropertySheet::CPropertySheet()
  593. {
  594.     CommonConstruct(NULL, 0);
  595. }
  596.  
  597. CPropertySheet::CPropertySheet(UINT nIDCaption, CWnd* pParentWnd,
  598.     UINT iSelectPage)
  599. {
  600.     ASSERT(nIDCaption != 0);
  601.  
  602.     VERIFY(m_strCaption.LoadString(nIDCaption) != 0);
  603.     CommonConstruct(pParentWnd, iSelectPage);
  604. }
  605.  
  606. CPropertySheet::CPropertySheet(LPCTSTR pszCaption, CWnd* pParentWnd,
  607.     UINT iSelectPage)
  608. {
  609.     ASSERT(pszCaption != NULL);
  610.  
  611.     m_strCaption = pszCaption;
  612.     CommonConstruct(pParentWnd, iSelectPage);
  613. }
  614.  
  615. void CPropertySheet::Construct(UINT nIDCaption, CWnd* pParentWnd,
  616.     UINT iSelectPage)
  617. {
  618.     ASSERT(nIDCaption != 0);
  619.  
  620.     VERIFY(m_strCaption.LoadString(nIDCaption) != 0);
  621.     CommonConstruct(pParentWnd, iSelectPage);
  622. }
  623.  
  624. void CPropertySheet::Construct(LPCTSTR pszCaption, CWnd* pParentWnd,
  625.     UINT iSelectPage)
  626. {
  627.     ASSERT(pszCaption != NULL);
  628.  
  629.     m_strCaption = pszCaption;
  630.     CommonConstruct(pParentWnd, iSelectPage);
  631. }
  632.  
  633. void CPropertySheet::CommonConstruct(CWnd* pParentWnd, UINT iSelectPage)
  634. {
  635.     memset(&m_psh, 0, sizeof(m_psh));
  636.     m_psh.dwSize = sizeof(m_psh);
  637.     m_psh.dwFlags = PSH_PROPSHEETPAGE | WCE_IF(PSH_NOAPPLYNOW, 0);
  638.     m_psh.pszCaption = m_strCaption;
  639.     m_psh.nStartPage = iSelectPage;
  640.     m_bStacked = TRUE;
  641.     m_bModeless = FALSE;
  642.  
  643.     if (AfxHelpEnabled())
  644.         m_psh.dwFlags |= PSH_HASHELP;
  645.  
  646.     m_pParentWnd = pParentWnd;  // m_psh.hwndParent set in DoModal/create
  647. }
  648.  
  649. void CPropertySheet::EnableStackedTabs(BOOL bStacked)
  650. {
  651.     m_bStacked = bStacked;
  652. }
  653.  
  654. void CPropertySheet::SetTitle(LPCTSTR lpszText, UINT nStyle)
  655. {
  656.     ASSERT((nStyle & ~PSH_PROPTITLE) == 0); // only PSH_PROPTITLE is valid
  657.     ASSERT(lpszText == NULL || AfxIsValidString(lpszText));
  658.  
  659.     if (m_hWnd == NULL)
  660.     {
  661.         AFX_OLDPROPSHEETHEADER* psh = GetPropSheetHeader();
  662.         // set internal state
  663.         m_strCaption = lpszText;
  664.         psh->pszCaption = m_strCaption;
  665.         psh->dwFlags &= ~PSH_PROPTITLE;
  666.         psh->dwFlags |= nStyle;
  667.     }
  668.     else
  669.     {
  670.         // set external state
  671.         SendMessage(PSM_SETTITLE, nStyle, (LPARAM)lpszText);
  672.     }
  673. }
  674.  
  675. CPropertySheet::~CPropertySheet()
  676. {
  677.     delete[] (PROPSHEETPAGE*)m_psh.ppsp;
  678. }
  679.  
  680. BOOL CPropertySheet::PreTranslateMessage(MSG* pMsg)
  681. {
  682.     ASSERT_VALID(this);
  683.  
  684.     // allow tooltip messages to be filtered
  685.     if (CWnd::PreTranslateMessage(pMsg))
  686.         return TRUE;
  687.  
  688.     // allow sheet to translate Ctrl+Tab, Shift+Ctrl+Tab,
  689.     //  Ctrl+PageUp, and Ctrl+PageDown
  690.     if (pMsg->message == WM_KEYDOWN && GetAsyncKeyState(VK_CONTROL) < 0 &&
  691.         (pMsg->wParam == VK_TAB || pMsg->wParam == VK_PRIOR || pMsg->wParam == VK_NEXT))
  692.     {
  693.         if (SendMessage(PSM_ISDIALOGMESSAGE, 0, (LPARAM)pMsg))
  694.             return TRUE;
  695.     }
  696.  
  697.     // handle rest with IsDialogMessage
  698.     return PreTranslateInput(pMsg);
  699. }
  700.  
  701. BOOL CPropertySheet::OnCmdMsg(UINT nID, int nCode, void* pExtra,
  702.     AFX_CMDHANDLERINFO* pHandlerInfo)
  703. {
  704.     if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
  705.         return TRUE;
  706.  
  707.     if ((nCode != CN_COMMAND && nCode != CN_UPDATE_COMMAND_UI) ||
  708.             !IS_COMMAND_ID(nID) || nID >= 0xf000)
  709.     {
  710.         // control notification or non-command button or system command
  711.         return FALSE;       // not routed any further
  712.     }
  713.  
  714.     // if we have an owner window, give it second crack
  715.     CWnd* pOwner = GetParent();
  716.     if (pOwner != NULL)
  717.     {
  718. #ifdef _DEBUG
  719.         if (afxTraceFlags & traceCmdRouting)
  720.             TRACE1("Routing command id 0x%04X to owner window.\n", nID);
  721. #endif
  722.         ASSERT(pOwner != this);
  723.         if (pOwner->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
  724.             return TRUE;
  725.     }
  726.  
  727.     // last crack goes to the current CWinThread object
  728.     CWinThread* pThread = AfxGetThread();
  729.     if (pThread != NULL)
  730.     {
  731. #ifdef _DEBUG
  732.         if (afxTraceFlags & traceCmdRouting)
  733.             TRACE1("Routing command id 0x%04X to app.\n", nID);
  734. #endif
  735.         if (pThread->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
  736.             return TRUE;
  737.     }
  738.  
  739. #ifdef _DEBUG
  740.     if (afxTraceFlags & traceCmdRouting)
  741.     {
  742.         TRACE2("IGNORING command id 0x%04X sent to %hs dialog.\n", nID,
  743.                 GetRuntimeClass()->m_lpszClassName);
  744.     }
  745. #endif
  746.     return FALSE;
  747. }
  748.  
  749. CPropertyPage* CPropertySheet::GetActivePage() const
  750. {
  751.     ASSERT_VALID(this);
  752.  
  753.     CPropertyPage* pPage;
  754.     if (m_hWnd != NULL)
  755.         pPage = STATIC_DOWNCAST(CPropertyPage,
  756.             CWnd::FromHandle((HWND)::SendMessage(m_hWnd, PSM_GETCURRENTPAGEHWND, 0, 0)));
  757.     else
  758.         pPage = GetPage(GetActiveIndex());
  759.     return pPage;
  760. }
  761.  
  762. BOOL CPropertySheet::ContinueModal()
  763. {
  764.     // allow CWnd::EndModalLoop to be used
  765.     if (!CWnd::ContinueModal())
  766.         return FALSE;
  767.  
  768.     // when active page is NULL, the modal loop should end
  769.     ASSERT(::IsWindow(m_hWnd));
  770.     BOOL bResult = SendMessage(PSM_GETCURRENTPAGEHWND);
  771.     return bResult;
  772. }
  773.  
  774. int CPropertySheet::DoModal()
  775. {
  776.     ASSERT_VALID(this);
  777.     ASSERT(m_hWnd == NULL);
  778.  
  779.     // register common controls
  780.     VERIFY(AfxDeferRegisterClass(AFX_WNDCOMMCTLS_REG));
  781. #if !defined(_WIN32_WCE)
  782.     AfxDeferRegisterClass(AFX_WNDCOMMCTLSNEW_REG);
  783. #endif // _WIN32_WCE
  784.  
  785.     // finish building PROPSHEETHEADER structure
  786.     BuildPropPageArray();
  787.  
  788.     // allow OLE servers to disable themselves
  789.     CWinApp* pApp = AfxGetApp();
  790.     if (pApp != NULL)
  791.         pApp->EnableModeless(FALSE);
  792.  
  793.     // find parent HWND
  794.     HWND hWndTop;
  795.     HWND hWndParent = CWnd::GetSafeOwner_(m_pParentWnd->GetSafeHwnd(), &hWndTop);
  796.     AFX_OLDPROPSHEETHEADER* psh = GetPropSheetHeader();
  797.     psh->hwndParent = hWndParent;
  798.     BOOL bEnableParent = FALSE;
  799.     if (hWndParent != NULL && ::IsWindowEnabled(hWndParent))
  800.     {
  801.         ::EnableWindow(hWndParent, FALSE);
  802.         bEnableParent = TRUE;
  803.     }
  804.     HWND hWndCapture = ::GetCapture();
  805.     if (hWndCapture != NULL)
  806.         ::SendMessage(hWndCapture, WM_CANCELMODE, 0, 0);
  807.  
  808.     // setup for modal loop and creation
  809.     m_nModalResult = 0;
  810.     m_nFlags |= WF_CONTINUEMODAL;
  811.  
  812.     // hook for creation of window
  813.     AfxHookWindowCreate(this);
  814. #if defined(_WIN32_WCE)
  815. // WinCE: Tweak the m_pWndInit... we reserve that for the dialog pages.
  816.     _AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
  817.     pThreadState->m_pWndInitPropertySheet = pThreadState->m_pWndInit;
  818.     pThreadState->m_pWndInit = NULL;    
  819. #endif // _WIN32_WCE
  820.     psh->dwFlags |= PSH_MODELESS;
  821.     m_nFlags |= WF_CONTINUEMODAL;
  822.     HWND hWnd = (HWND)::PropertySheet((AFX_OLDPROPSHEETHEADER*)psh);
  823. #ifdef _DEBUG
  824.     DWORD dwError = ::GetLastError();
  825. #endif
  826.     psh->dwFlags &= ~PSH_MODELESS;
  827.     AfxUnhookWindowCreate();
  828.  
  829.     // handle error
  830.     if (hWnd == NULL || hWnd == (HWND)-1)
  831.     {
  832.         TRACE1("PropertySheet() failed: GetLastError returned %d\n", dwError);
  833.         m_nFlags &= ~WF_CONTINUEMODAL;
  834.     }
  835.  
  836.     int nResult = m_nModalResult;
  837.     if (ContinueModal())
  838.     {
  839.         // enter modal loop
  840.         DWORD dwFlags = MLF_SHOWONIDLE;
  841.         if (GetStyle() & DS_NOIDLEMSG)
  842.             dwFlags |= MLF_NOIDLEMSG;
  843.         nResult = RunModalLoop(dwFlags);
  844.     }
  845.  
  846.     // hide the window before enabling parent window, etc.
  847.     if (m_hWnd != NULL)
  848.     {
  849.         SetWindowPos(NULL, 0, 0, 0, 0, SWP_HIDEWINDOW|
  850.             SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
  851.     }
  852.     if (bEnableParent)
  853.         ::EnableWindow(hWndParent, TRUE);
  854.     if (hWndParent != NULL && ::GetActiveWindow() == m_hWnd)
  855.         ::SetActiveWindow(hWndParent);
  856.  
  857.     // cleanup
  858.     DestroyWindow();
  859.  
  860.     // allow OLE servers to enable themselves
  861.     if (pApp != NULL)
  862.         pApp->EnableModeless(TRUE);
  863.     if (hWndTop != NULL)
  864.         ::EnableWindow(hWndTop, TRUE);
  865.  
  866.     return nResult;
  867. }
  868.  
  869. int CALLBACK
  870. AfxPropSheetCallback(HWND, UINT message, LPARAM lParam)
  871. {
  872.     switch (message)
  873.     {
  874.     case PSCB_PRECREATE:
  875.         {
  876.             _AFX_THREAD_STATE* pState = AfxGetThreadState();
  877.             LPDLGTEMPLATE lpTemplate = (LPDLGTEMPLATE)lParam;
  878.             if (lpTemplate->style != pState->m_dwPropStyle ||
  879.                 lpTemplate->dwExtendedStyle != pState->m_dwPropExStyle)
  880.             {
  881.                 // Mark the dialog template as read-write.
  882.                 DWORD dwOldProtect;
  883.                 VirtualProtect(lpTemplate, sizeof(DLGTEMPLATE), PAGE_READWRITE, &dwOldProtect);
  884.  
  885.                 // Ensure DS_SETFONT is set correctly.
  886.                 lpTemplate->style = lpTemplate->style & DS_SETFONT ?
  887.                                     pState->m_dwPropStyle | DS_SETFONT :
  888.                                     pState->m_dwPropStyle & ~DS_SETFONT;
  889.  
  890.                 lpTemplate->dwExtendedStyle = pState->m_dwPropExStyle;
  891.                 return TRUE;
  892.             }
  893.             return FALSE;
  894.         }
  895.     }
  896.  
  897.     return 0;
  898. }
  899.  
  900. BOOL CPropertySheet::Create(CWnd* pParentWnd, DWORD dwStyle, DWORD dwExStyle)
  901. {
  902.     _AFX_THREAD_STATE* pState = AfxGetThreadState();
  903.  
  904.     // Calculate the default window style.
  905.     if (dwStyle == (DWORD)-1)
  906.     {
  907.         pState->m_dwPropStyle = DS_MODALFRAME | DS_3DLOOK | DS_CONTEXTHELP |
  908.                     WCE_IF(DS_CENTER, 0) | DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION;
  909.  
  910.         // Wizards don't have WS_SYSMENU.
  911.         if (!IsWizard())
  912.             pState->m_dwPropStyle |= WS_SYSMENU;
  913.     }
  914.     else
  915.     {
  916.         pState->m_dwPropStyle = dwStyle;
  917.     }
  918.     pState->m_dwPropExStyle = dwExStyle;
  919.  
  920.     ASSERT_VALID(this);
  921.     ASSERT(m_hWnd == NULL);
  922.  
  923.     VERIFY(AfxDeferRegisterClass(AFX_WNDCOMMCTLS_REG));
  924. #if !defined(_WIN32_WCE)
  925.     AfxDeferRegisterClass(AFX_WNDCOMMCTLSNEW_REG);
  926. #endif // _WIN32_WCE
  927.  
  928.     // finish building PROPSHEETHEADER structure
  929.     AFX_OLDPROPSHEETHEADER* psh = GetPropSheetHeader();
  930.  
  931.     BuildPropPageArray();
  932.     m_bModeless = TRUE;
  933.     psh->dwFlags |= (PSH_MODELESS|PSH_USECALLBACK);
  934.     psh->pfnCallback = WCE_IF(NULL, AfxPropSheetCallback);
  935.     psh->hwndParent = pParentWnd->GetSafeHwnd();
  936.  
  937.     // hook the window creation process
  938.     AfxHookWindowCreate(this);
  939. #if defined(_WIN32_WCE)
  940. // WinCE: Tweak the m_pWndInit... we reserve that for the dialog pages.
  941.     _AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
  942.     pThreadState->m_pWndInitPropertySheet = pThreadState->m_pWndInit;
  943.     pThreadState->m_pWndInit = NULL;    
  944. #endif // _WIN32_WCE
  945.     HWND hWnd = (HWND)PropertySheet((PROPSHEETHEADER*)psh);
  946. #ifdef _DEBUG
  947.     DWORD dwError = ::GetLastError();
  948. #endif
  949.  
  950.     // cleanup on failure, otherwise return TRUE
  951.     if (!AfxUnhookWindowCreate())
  952.         PostNcDestroy();    // cleanup if Create fails
  953.  
  954.     if (hWnd == NULL || hWnd == (HWND)-1)
  955.     {
  956.         TRACE1("PropertySheet() failed: GetLastError returned %d\n", dwError);
  957.         return FALSE;
  958.     }
  959.  
  960.     ASSERT(hWnd == m_hWnd);
  961.     return TRUE;
  962. }
  963.  
  964. void CPropertySheet::BuildPropPageArray()
  965. {
  966.     // delete existing prop page array
  967.     delete[] (PROPSHEETPAGE*)m_psh.ppsp;
  968.     m_psh.ppsp = NULL;
  969.  
  970.     // build new prop page array
  971.     AFX_OLDPROPSHEETPAGE* ppsp = new AFX_OLDPROPSHEETPAGE[m_pages.GetSize()];
  972.     m_psh.ppsp = (LPPROPSHEETPAGE)ppsp;
  973.     BOOL bWizard = WCE_IF(FALSE,(m_psh.dwFlags & (PSH_WIZARD | PSH_WIZARD97)));
  974.     for (int i = 0; i < m_pages.GetSize(); i++)
  975.     {
  976.         CPropertyPage* pPage = GetPage(i);
  977.         memcpy(&ppsp[i], &pPage->m_psp, sizeof(pPage->m_psp));
  978. #if !defined(_WIN32_WCE)
  979. // WinCE:  there are two problems with PreProcessPageTemplate: changing fonts doesn't work (not really necessary),
  980. // and LockResource() gives us read-only memory with the PSP_DLGINDIRECT bit set.  According to PROPSHEETPAGE docs,
  981. // this is an invalid combination that will cause a memory exception in COMMCTRL.DLL
  982.         pPage->PreProcessPageTemplate((PROPSHEETPAGE&)ppsp[i], bWizard);
  983. #endif // _WIN32_WCE
  984.     }
  985.     m_psh.nPages = m_pages.GetSize();
  986. }
  987.  
  988. ////////////////////////////////////////////////////////////////////////////
  989.  
  990. int CPropertySheet::GetPageCount() const
  991. {
  992.     ASSERT_VALID(this);
  993.  
  994.     if (m_hWnd == NULL)
  995.         return m_pages.GetSize();
  996.  
  997.     CTabCtrl* pTab = GetTabControl();
  998.     ASSERT_VALID(pTab);
  999.     return pTab->GetItemCount();
  1000. }
  1001.  
  1002. int CPropertySheet::GetActiveIndex() const
  1003. {
  1004.     if (m_hWnd == NULL)
  1005.         return ((CPropertySheet*)this)->GetPropSheetHeader()->nStartPage;
  1006.  
  1007.     CTabCtrl* pTab = GetTabControl();
  1008.     ASSERT_VALID(pTab);
  1009.     return pTab->GetCurSel();
  1010. }
  1011.  
  1012. BOOL CPropertySheet::SetActivePage(int nPage)
  1013. {
  1014.     if (m_hWnd == NULL)
  1015.     {
  1016.         GetPropSheetHeader()->nStartPage = nPage;
  1017.         return TRUE;
  1018.     }
  1019.     return (BOOL)SendMessage(PSM_SETCURSEL, nPage);
  1020. }
  1021.  
  1022. int CPropertySheet::GetPageIndex(CPropertyPage* pPage)
  1023. {
  1024.     for (int i = 0; i < GetPageCount(); i++)
  1025.     {
  1026.         if (GetPage(i) == pPage)
  1027.             return i;
  1028.     }
  1029.     return -1;  // pPage not found
  1030. }
  1031.  
  1032. BOOL CPropertySheet::SetActivePage(CPropertyPage* pPage)
  1033. {
  1034.     ASSERT_VALID(this);
  1035.     ASSERT(pPage != NULL);
  1036.     ASSERT_KINDOF(CPropertyPage, pPage);
  1037.  
  1038.     int nPage = GetPageIndex(pPage);
  1039.     ASSERT(pPage >= 0);
  1040.  
  1041.     return SetActivePage(nPage);
  1042. }
  1043.  
  1044. void CPropertySheet::AddPage(CPropertyPage* pPage)
  1045. {
  1046.     ASSERT_VALID(this);
  1047.     ASSERT(pPage != NULL);
  1048.     ASSERT_KINDOF(CPropertyPage, pPage);
  1049.     ASSERT_VALID(pPage);
  1050.  
  1051.     // add page to internal list
  1052.     m_pages.Add(pPage);
  1053.  
  1054.     // add page externally
  1055.     if (m_hWnd != NULL)
  1056.     {
  1057.         // build new prop page array
  1058.         AFX_OLDPROPSHEETPAGE *ppsp = new AFX_OLDPROPSHEETPAGE[m_pages.GetSize()];
  1059.         memcpy(ppsp, m_psh.ppsp, sizeof(AFX_OLDPROPSHEETPAGE) * (m_pages.GetSize()-1));
  1060.         delete[] (PROPSHEETPAGE*)m_psh.ppsp;
  1061.         m_psh.ppsp = (PROPSHEETPAGE*)ppsp;
  1062.         ppsp += m_pages.GetSize()-1;
  1063.  
  1064.         // copy processed PROPSHEETPAGE struct to end
  1065.         memcpy(ppsp, &pPage->m_psp, sizeof(pPage->m_psp));
  1066. #if !defined(_WIN32_WCE)
  1067.         pPage->PreProcessPageTemplate((PROPSHEETPAGE&)*ppsp, IsWizard());
  1068. #endif // _WIN32_WCE
  1069.         HPROPSHEETPAGE hPSP = CreatePropertySheetPage((PROPSHEETPAGE*)ppsp);
  1070.         if (hPSP == NULL)
  1071.             AfxThrowMemoryException();
  1072.  
  1073.         if (!SendMessage(PSM_ADDPAGE, 0, (LPARAM)hPSP))
  1074.         {
  1075.             DestroyPropertySheetPage(hPSP);
  1076.             AfxThrowMemoryException();
  1077.         }
  1078. #if defined(_WIN32_WCE)
  1079.         wce_AdjustPropertySheetSize(this);
  1080. #endif
  1081.     }
  1082. }
  1083.  
  1084. void CPropertySheet::RemovePage(CPropertyPage* pPage)
  1085. {
  1086.     ASSERT_VALID(this);
  1087.     ASSERT(pPage != NULL);
  1088.     ASSERT_KINDOF(CPropertyPage, pPage);
  1089.  
  1090.     int nPage = GetPageIndex(pPage);
  1091.     ASSERT(nPage >= 0);
  1092.  
  1093.     RemovePage(nPage);
  1094. }
  1095.  
  1096. void CPropertySheet::RemovePage(int nPage)
  1097. {
  1098.     ASSERT_VALID(this);
  1099.  
  1100.     // remove the page externally
  1101.     if (m_hWnd != NULL)
  1102.         SendMessage(PSM_REMOVEPAGE, nPage);
  1103.  
  1104.     // remove the page from internal list
  1105.     m_pages.RemoveAt(nPage);
  1106. }
  1107.  
  1108. void CPropertySheet::EndDialog(int nEndID)
  1109. {
  1110.     ASSERT_VALID(this);
  1111.  
  1112.     CWnd::EndModalLoop(nEndID);
  1113.     if (m_bModeless)
  1114.         DestroyWindow();
  1115.     else
  1116.         PostMessage(PSM_PRESSBUTTON, PSBTN_CANCEL);
  1117. }
  1118.  
  1119. void CPropertySheet::OnClose()
  1120. {
  1121.     m_nModalResult = IDCANCEL;
  1122.     if (m_bModeless)
  1123.         DestroyWindow();
  1124.     else
  1125.         Default();
  1126. }
  1127.  
  1128. void CPropertySheet::OnSysCommand(UINT nID, LPARAM)
  1129. {
  1130.     m_nModalResult = IDCANCEL;
  1131.     switch (nID & 0xFFF0)
  1132.     {
  1133.     case SC_CLOSE:
  1134.         if (m_bModeless)
  1135.         {
  1136.             SendMessage(WM_CLOSE);
  1137.             return;
  1138.         }
  1139.     }
  1140.     Default();
  1141. }
  1142.  
  1143. /////////////////////////////////////////////////////////////////////////////
  1144. // CPropertySheet message handlers
  1145.  
  1146. AFX_STATIC_DATA int _afxPropSheetButtons[] = { IDOK, IDCANCEL, ID_APPLY_NOW, IDHELP };
  1147.  
  1148. BOOL CPropertySheet::OnInitDialog()
  1149. {
  1150.     // change tab style if scrolling tabs desired (stacked tabs are default)
  1151.     if (!m_bStacked)
  1152.     {
  1153.         HWND hWndTab = (HWND)::GetDlgItem(m_hWnd, AFX_IDC_TAB_CONTROL);
  1154.         if (hWndTab != NULL)
  1155.             CWnd::ModifyStyle(hWndTab, TCS_MULTILINE, TCS_SINGLELINE, 0);
  1156.     }
  1157.  
  1158. #if defined(_WIN32_WCE)
  1159.     BOOL bResult = TRUE;
  1160.  
  1161.     wce_AdjustPropertySheetSize(this);
  1162. #else // _WIN32_WCE
  1163.     if (!IsWizard())
  1164.     {
  1165.         // resize the tab control so the layout is less restrictive
  1166.         HWND hWnd = (HWND)::GetDlgItem(m_hWnd, AFX_IDC_TAB_CONTROL);
  1167.         ASSERT(hWnd != NULL);
  1168.         CRect rectOld;
  1169.         ::GetWindowRect(hWnd, &rectOld);
  1170.         ScreenToClient(rectOld);
  1171.         CRect rectNew(0, 0, 0, 32);
  1172.         ::MapDialogRect(m_hWnd, &rectNew);
  1173.         if (rectNew.bottom < rectOld.bottom)
  1174.         {
  1175.             // move tab control
  1176.             int cyDiff = rectOld.Height() - rectNew.bottom;
  1177.             ::SetWindowPos(hWnd, NULL, 0, 0, rectOld.Width(), rectNew.bottom,
  1178.                 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
  1179.  
  1180.             // move buttons by similar amount
  1181.             for (int i = 0; i < _countof(_afxPropSheetButtons); i++)
  1182.             {
  1183.                 hWnd = ::GetDlgItem(m_hWnd, _afxPropSheetButtons[i]);
  1184.                 if (hWnd != NULL)
  1185.                 {
  1186.                     ::GetWindowRect(hWnd, &rectOld);
  1187.                     ScreenToClient(&rectOld);
  1188.                     ::SetWindowPos(hWnd, NULL,
  1189.                         rectOld.left, rectOld.top - cyDiff,
  1190.                         0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
  1191.                 }
  1192.             }
  1193.  
  1194.             // resize property sheet itself similarly
  1195.             GetWindowRect(&rectOld);
  1196.             SetWindowPos(NULL, 0, 0, rectOld.Width(), rectOld.Height() - cyDiff,
  1197.                 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
  1198.         }
  1199.     }
  1200.  
  1201.     BOOL bResult = (BOOL)Default();
  1202.  
  1203.     if (m_bModeless && !IsWizard())
  1204.     {
  1205.         // layout property sheet so button area is not accounted for
  1206.         CRect rectWnd;
  1207.         GetWindowRect(rectWnd);
  1208.         CRect rectButton;
  1209.         HWND hWnd = ::GetDlgItem(m_hWnd, IDOK);
  1210.         ASSERT(hWnd != NULL);
  1211.         ::GetWindowRect(hWnd, rectButton);
  1212.         SetWindowPos(NULL, 0, 0,
  1213.             rectWnd.Width(), rectButton.top - rectWnd.top,
  1214.             SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
  1215.  
  1216.         // remove standard buttons for modeless dialogs
  1217.         for (int i = 0; i < _countof(_afxPropSheetButtons); i++)
  1218.         {
  1219.             HWND hWnd = ::GetDlgItem(m_hWnd, _afxPropSheetButtons[i]);
  1220.             if (hWnd != NULL)
  1221.             {
  1222.                 ::ShowWindow(hWnd, SW_HIDE);
  1223.                 ::EnableWindow(hWnd, FALSE);
  1224.             }
  1225.         }
  1226.     }
  1227. #endif // _WIN32_WCE
  1228.  
  1229.     // center the property sheet relative to the parent window
  1230.     if (!(GetStyle() & WS_CHILD))
  1231.         CenterWindow();
  1232.  
  1233.     return bResult;
  1234. }
  1235.  
  1236. #if !defined(_WIN32_WCE)
  1237. BOOL CPropertySheet::OnNcCreate(LPCREATESTRUCT)
  1238. {
  1239.     // By default, MFC does not directly support the new style
  1240.     // help button in the caption, so it is turned off here.
  1241.     // It can be added back in and implemented by derived classes
  1242.     // from CPropertySheet.
  1243.     ModifyStyleEx(WS_EX_CONTEXTHELP, 0);
  1244.  
  1245.     return (BOOL)Default();
  1246. }
  1247. #endif // _WIN32_WCE
  1248.  
  1249. LRESULT CPropertySheet::HandleInitDialog(WPARAM, LPARAM)
  1250. {
  1251.     LRESULT lResult = OnInitDialog();
  1252.     return lResult;
  1253. }
  1254.  
  1255. BOOL CPropertySheet::OnCommand(WPARAM wParam, LPARAM lParam)
  1256. {
  1257.     // allow message map override
  1258.     if (CWnd::OnCommand(wParam, lParam))
  1259.         return TRUE;
  1260.  
  1261.     // crack message parameters
  1262.     UINT nID = LOWORD(wParam);
  1263.     HWND hWndCtrl = (HWND)lParam;
  1264.     int nCode = HIWORD(wParam);
  1265.  
  1266. #if defined(_WIN32_WCE)
  1267. // WinCE: The OK button is somewhere else (i.e. hWndCtrl isn't set).  
  1268. // Check for it differently (and without the robustness).
  1269.     if (nID == IDOK)
  1270.     {
  1271.         if(m_bModeless)
  1272.             EndDialog(nID);
  1273.         else
  1274.             m_nModalResult = nID;
  1275.     }
  1276.     else
  1277. #endif // _WIN32_WCE    
  1278.     // set m_nModalResult to ID of button, whenever button is clicked
  1279.     if (hWndCtrl != NULL && nCode == BN_CLICKED)
  1280.     {
  1281.         if (::SendMessage(hWndCtrl, WM_GETDLGCODE, 0, 0) &
  1282.             (DLGC_BUTTON|DLGC_DEFPUSHBUTTON))
  1283.         {
  1284.             LONG lStyle = ::GetWindowLong(hWndCtrl, GWL_STYLE) & 0x0F;
  1285.             if (lStyle == BS_PUSHBUTTON || lStyle == BS_DEFPUSHBUTTON ||
  1286.                 lStyle == BS_USERBUTTON || lStyle == BS_OWNERDRAW)
  1287.             {
  1288.                 m_nModalResult = nID;
  1289.             }
  1290.         }
  1291.     }
  1292.     return FALSE;
  1293. }
  1294.  
  1295. LRESULT CPropertySheet::OnCommandHelp(WPARAM wParam, LPARAM lParam)
  1296. {
  1297.     ASSERT_VALID(this);
  1298.  
  1299.     CPropertyPage* pPage = GetActivePage();
  1300.     ASSERT_VALID(pPage);
  1301.     return AfxCallWndProc(pPage, pPage->m_hWnd, WM_COMMANDHELP, wParam, lParam);
  1302. }
  1303.  
  1304. HBRUSH CPropertySheet::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
  1305. {
  1306.     LRESULT lResult;
  1307.     if (pWnd->SendChildNotifyLastMsg(&lResult))
  1308.         return (HBRUSH)lResult;
  1309.  
  1310.     if (afxData.bWin4)
  1311.         return CWnd::OnCtlColor(pDC, pWnd, nCtlColor);
  1312.  
  1313.     if (!GrayCtlColor(pDC->m_hDC, pWnd->GetSafeHwnd(), nCtlColor,
  1314.       afxData.hbrBtnFace, afxData.clrBtnText))
  1315.         return (HBRUSH)Default();
  1316.     return afxData.hbrBtnFace;
  1317. }
  1318.  
  1319. /////////////////////////////////////////////////////////////////////////////
  1320. // CPropertySheet Diagnostics
  1321.  
  1322. #ifdef _DEBUG
  1323. void CPropertySheet::AssertValid() const
  1324. {
  1325.     CWnd::AssertValid();
  1326.     m_pages.AssertValid();
  1327.     ASSERT(m_psh.dwSize == sizeof(m_psh));
  1328.     ASSERT((m_psh.dwFlags & PSH_PROPSHEETPAGE) == PSH_PROPSHEETPAGE);
  1329. }
  1330.  
  1331. void CPropertySheet::Dump(CDumpContext& dc) const
  1332. {
  1333.     CWnd::Dump(dc);
  1334.  
  1335.     dc << "m_strCaption = " << m_strCaption << "\n";
  1336.     dc << "Number of Pages = " << m_pages.GetSize() << "\n";
  1337.     dc << "Stacked = " << m_bStacked << "\n";
  1338.     dc << "Modeless = " << m_bModeless << "\n";
  1339. }
  1340. #endif //_DEBUG
  1341.  
  1342. #if !defined(_WIN32_WCE)
  1343. ////////////////////////////////////////////////////////////////////////////
  1344. // CPropertyPageEx -- one page of a tabbed dialog, extended for IE4 features
  1345.  
  1346. CPropertyPageEx::CPropertyPageEx(UINT nIDTemplate, UINT nIDCaption, UINT nIDHeaderTitle, UINT nIDHeaderSubTitle)
  1347. {
  1348.     ASSERT(nIDTemplate != 0);
  1349.     CommonConstruct(MAKEINTRESOURCE(nIDTemplate), nIDCaption, nIDHeaderTitle, nIDHeaderSubTitle);
  1350. }
  1351.  
  1352. CPropertyPageEx::CPropertyPageEx(LPCTSTR lpszTemplateName, UINT nIDCaption, UINT nIDHeaderTitle, UINT nIDHeaderSubTitle)
  1353. {
  1354.     ASSERT(AfxIsValidString(lpszTemplateName));
  1355.     CommonConstruct(lpszTemplateName, nIDCaption, nIDHeaderTitle, nIDHeaderSubTitle);
  1356. }
  1357.  
  1358. void CPropertyPageEx::Construct(UINT nIDTemplate, UINT nIDCaption, UINT nIDHeaderTitle, UINT nIDHeaderSubTitle)
  1359. {
  1360.     ASSERT(nIDTemplate != 0);
  1361.     CommonConstruct(MAKEINTRESOURCE(nIDTemplate), nIDCaption, nIDHeaderTitle, nIDHeaderSubTitle);
  1362. }
  1363.  
  1364. void CPropertyPageEx::Construct(LPCTSTR lpszTemplateName, UINT nIDCaption, UINT nIDHeaderTitle, UINT nIDHeaderSubTitle)
  1365. {
  1366.     ASSERT(HIWORD(lpszTemplateName) == 0 ||
  1367.         AfxIsValidString(lpszTemplateName));
  1368.     CommonConstruct(lpszTemplateName, nIDCaption, nIDHeaderTitle, nIDHeaderSubTitle);
  1369. }
  1370.  
  1371. CPropertyPageEx::CPropertyPageEx()
  1372. {
  1373.     CommonConstruct(NULL, 0, 0, 0);
  1374. }
  1375.  
  1376. void CPropertyPageEx::CommonConstruct(LPCTSTR lpszTemplateName, UINT nIDCaption, UINT nIDHeaderTitle, UINT nIDHeaderSubTitle)
  1377. {
  1378.     memset(&m_psp, 0, sizeof(m_psp));
  1379.     m_psp.dwSize = sizeof(m_psp);
  1380.     m_psp.dwFlags = PSP_USECALLBACK;
  1381.     if (lpszTemplateName != NULL)
  1382.         m_psp.hInstance = AfxFindResourceHandle(lpszTemplateName, RT_DIALOG);
  1383.     m_psp.pszTemplate = lpszTemplateName;
  1384.     m_psp.pfnDlgProc = AfxDlgProc;
  1385.     m_psp.lParam = (LPARAM)this;
  1386.     m_psp.pfnCallback = AfxPropPageCallback;
  1387.     if (nIDCaption != 0)
  1388.     {
  1389.         VERIFY(m_strCaption.LoadString(nIDCaption));
  1390.         m_psp.pszTitle = m_strCaption;
  1391.         m_psp.dwFlags |= PSP_USETITLE;
  1392.     }
  1393.     if (nIDHeaderTitle != 0)
  1394.         VERIFY(m_strHeaderTitle.LoadString(nIDHeaderTitle));
  1395.     if (nIDHeaderSubTitle != 0)
  1396.         VERIFY(m_strHeaderSubTitle.LoadString(nIDHeaderSubTitle));
  1397.     if (AfxHelpEnabled())
  1398.         m_psp.dwFlags |= PSP_HASHELP;
  1399.     if (HIWORD(lpszTemplateName) == 0)
  1400.         m_nIDHelp = LOWORD((DWORD)lpszTemplateName);
  1401.     m_lpszTemplateName = m_psp.pszTemplate;
  1402.     m_bFirstSetActive = TRUE;
  1403. }
  1404.  
  1405. #ifdef _DEBUG
  1406. void CPropertyPageEx::AssertValid() const
  1407. {
  1408.     CDialog::AssertValid();
  1409.     ASSERT(m_psp.dwSize == sizeof(m_psp));
  1410.     ASSERT(m_psp.dwFlags & PSP_USECALLBACK);
  1411.     ASSERT(m_psp.pfnDlgProc == AfxDlgProc);
  1412.     ASSERT(m_psp.lParam == (LPARAM)this);
  1413. }
  1414.  
  1415. void CPropertyPageEx::Dump(CDumpContext& dc) const
  1416. {
  1417.     CDialog::Dump(dc);
  1418.  
  1419.     dc << "m_strCaption = " << m_strCaption << "\n";
  1420.     dc << "m_psp.dwFlags = " << m_psp.dwFlags << "\n";
  1421.     dc << "m_strHeaderTitle = " << m_strHeaderTitle << "\n";
  1422.     dc << "m_strHeaderSubTitle = " << m_strHeaderSubTitle << "\n";
  1423. }
  1424. #endif //_DEBUG
  1425.  
  1426. /////////////////////////////////////////////////////////////////////////////
  1427. // CPropertySheetEx -- a tabbed "dialog" (really a popup-window), extended
  1428. //                     for IE4
  1429.  
  1430. CPropertySheetEx::CPropertySheetEx()
  1431. {
  1432.     CommonConstruct(NULL, 0, NULL, NULL, NULL);
  1433. }
  1434.  
  1435. CPropertySheetEx::CPropertySheetEx(UINT nIDCaption, CWnd* pParentWnd,
  1436.     UINT iSelectPage, HBITMAP hbmWatermark, HPALETTE hpalWatermark,
  1437.     HBITMAP hbmHeader)
  1438. {
  1439.     ASSERT(nIDCaption != 0);
  1440.  
  1441.     VERIFY(m_strCaption.LoadString(nIDCaption) != 0);
  1442.     CommonConstruct(pParentWnd, iSelectPage, hbmWatermark, hpalWatermark, hbmHeader);
  1443. }
  1444.  
  1445. CPropertySheetEx::CPropertySheetEx(LPCTSTR pszCaption, CWnd* pParentWnd,
  1446.     UINT iSelectPage, HBITMAP hbmWatermark, HPALETTE hpalWatermark,
  1447.     HBITMAP hbmHeader)
  1448. {
  1449.     ASSERT(pszCaption != NULL);
  1450.  
  1451.     m_strCaption = pszCaption;
  1452.     CommonConstruct(pParentWnd, iSelectPage, hbmWatermark, hpalWatermark, hbmHeader);
  1453. }
  1454.  
  1455. void CPropertySheetEx::Construct(UINT nIDCaption, CWnd* pParentWnd,
  1456.     UINT iSelectPage, HBITMAP hbmWatermark, HPALETTE hpalWatermark,
  1457.     HBITMAP hbmHeader)
  1458. {
  1459.     ASSERT(nIDCaption != 0);
  1460.  
  1461.     VERIFY(m_strCaption.LoadString(nIDCaption) != 0);
  1462.     CommonConstruct(pParentWnd, iSelectPage, hbmWatermark, hpalWatermark, hbmHeader);
  1463. }
  1464.  
  1465. void CPropertySheetEx::Construct(LPCTSTR pszCaption, CWnd* pParentWnd,
  1466.     UINT iSelectPage, HBITMAP hbmWatermark, HPALETTE hpalWatermark,
  1467.     HBITMAP hbmHeader)
  1468. {
  1469.     ASSERT(pszCaption != NULL);
  1470.  
  1471.     m_strCaption = pszCaption;
  1472.     CommonConstruct(pParentWnd, iSelectPage, hbmWatermark, hpalWatermark, hbmHeader);
  1473. }
  1474.  
  1475. void CPropertySheetEx::CommonConstruct(CWnd* pParentWnd, UINT iSelectPage,
  1476.     HBITMAP hbmWatermark, HPALETTE hpalWatermark, HBITMAP hbmHeader)
  1477. {
  1478.     memset(&m_psh, 0, sizeof(m_psh));
  1479.     m_psh.dwSize = sizeof(m_psh);
  1480.     m_psh.dwFlags = PSH_PROPSHEETPAGE;
  1481.     m_psh.pszCaption = m_strCaption;
  1482.     m_psh.nStartPage = iSelectPage;
  1483.     m_bStacked = TRUE;
  1484.     m_bModeless = FALSE;
  1485.  
  1486.     if (AfxHelpEnabled())
  1487.         m_psh.dwFlags |= PSH_HASHELP;
  1488.  
  1489.     if (hbmWatermark != NULL)
  1490.     {
  1491.         m_psh.hbmWatermark = hbmWatermark;
  1492.         m_psh.dwFlags |= (PSH_USEHBMWATERMARK | PSH_WATERMARK);
  1493.     }
  1494.     if (hpalWatermark != NULL)
  1495.     {
  1496.         m_psh.hplWatermark = hpalWatermark;
  1497.         m_psh.dwFlags |= PSH_USEHPLWATERMARK;
  1498.     }
  1499.     if (hbmHeader != NULL)
  1500.     {
  1501.         m_psh.hbmHeader = hbmHeader;
  1502.         m_psh.dwFlags |= (PSH_USEHBMHEADER | PSH_HEADER);
  1503.     }
  1504.     m_pParentWnd = pParentWnd;  // m_psh.hwndParent set in DoModal/create
  1505. }
  1506.  
  1507. void CPropertySheetEx::BuildPropPageArray()
  1508. {
  1509.     // delete existing prop page array
  1510.     delete[] (PROPSHEETPAGE*)m_psh.ppsp;
  1511.     m_psh.ppsp = NULL;
  1512.  
  1513.     // build new prop page array
  1514.     PROPSHEETPAGE* ppsp = new PROPSHEETPAGE[m_pages.GetSize()];
  1515.     m_psh.ppsp = (LPPROPSHEETPAGE)ppsp;
  1516.     BOOL bWizard = (m_psh.dwFlags & (PSH_WIZARD | PSH_WIZARD97));
  1517.     for (int i = 0; i < m_pages.GetSize(); i++)
  1518.     {
  1519.         CPropertyPage* pPage = GetPage(i);
  1520.         memcpy(&ppsp[i], &pPage->m_psp, sizeof(pPage->m_psp));
  1521.         ppsp[i].dwSize = sizeof(PROPSHEETPAGE);
  1522.         if (pPage->IsKindOf(RUNTIME_CLASS(CPropertyPageEx)))
  1523.         {
  1524.             CPropertyPageEx* pPageEx = (CPropertyPageEx*)pPage;
  1525.             if (!pPageEx->m_strHeaderTitle.IsEmpty())
  1526.             {
  1527.                 ppsp[i].pszHeaderTitle = pPageEx->m_strHeaderTitle;
  1528.                 ppsp[i].dwFlags |= PSP_USEHEADERTITLE;
  1529.             }
  1530.             if (!pPageEx->m_strHeaderSubTitle.IsEmpty())
  1531.             {
  1532.                 ppsp[i].pszHeaderSubTitle = pPageEx->m_strHeaderSubTitle;
  1533.                 ppsp[i].dwFlags |= PSP_USEHEADERSUBTITLE;
  1534.             }
  1535.         }
  1536. #if !defined(_WIN32_WCE)
  1537.         pPage->PreProcessPageTemplate(ppsp[i], bWizard);
  1538. #endif // _WIN32_WCE
  1539.     }
  1540.     m_psh.nPages = m_pages.GetSize();
  1541. }
  1542.  
  1543. CPropertySheetEx::~CPropertySheetEx()
  1544. {
  1545.     delete[] (PROPSHEETPAGE*)m_psh.ppsp;
  1546. }
  1547.  
  1548. void CPropertySheetEx::AddPage(CPropertyPageEx* pPage)
  1549. {
  1550.     ASSERT_VALID(this);
  1551.     ASSERT(pPage != NULL);
  1552.     ASSERT_KINDOF(CPropertyPageEx, pPage);
  1553.     ASSERT_VALID(pPage);
  1554.  
  1555.     // add page to internal list
  1556.     m_pages.Add(pPage);
  1557.  
  1558.     // add page externally
  1559.     if (m_hWnd != NULL)
  1560.     {
  1561.         // build new prop page array
  1562.         PROPSHEETPAGE *ppsp = new PROPSHEETPAGE[m_pages.GetSize()];
  1563.         memcpy(ppsp, m_psh.ppsp, sizeof(PROPSHEETPAGE) * (m_pages.GetSize()-1));
  1564.         delete[] (PROPSHEETPAGE*)m_psh.ppsp;
  1565.         m_psh.ppsp = ppsp;
  1566.         ppsp += m_pages.GetSize()-1;
  1567.  
  1568.         memcpy(ppsp, &pPage->m_psp, sizeof(pPage->m_psp));
  1569. #if !defined(_WIN32_WCE)
  1570.         pPage->PreProcessPageTemplate(*ppsp, IsWizard());
  1571. #endif // _WIN32_WCE
  1572.         if (!pPage->m_strHeaderTitle.IsEmpty())
  1573.         {
  1574.             ppsp->pszHeaderTitle = pPage->m_strHeaderTitle;
  1575.             ppsp->dwFlags |= PSP_USEHEADERTITLE;
  1576.         }
  1577.         if (!pPage->m_strHeaderSubTitle.IsEmpty())
  1578.         {
  1579.             ppsp->pszHeaderSubTitle = pPage->m_strHeaderSubTitle;
  1580.             ppsp->dwFlags |= PSP_USEHEADERSUBTITLE;
  1581.         }
  1582.         HPROPSHEETPAGE hPSP = CreatePropertySheetPage(ppsp);
  1583.         if (hPSP == NULL)
  1584.             AfxThrowMemoryException();
  1585.  
  1586.         if (!SendMessage(PSM_ADDPAGE, 0, (LPARAM)hPSP))
  1587.         {
  1588.             DestroyPropertySheetPage(hPSP);
  1589.             AfxThrowMemoryException();
  1590.         }
  1591.     }
  1592. }
  1593.  
  1594. /////////////////////////////////////////////////////////////////////////////
  1595. // CPropertySheetEx Diagnostics
  1596.  
  1597. #ifdef _DEBUG
  1598. void CPropertySheetEx::AssertValid() const
  1599. {
  1600.     CWnd::AssertValid();
  1601.     m_pages.AssertValid();
  1602.     ASSERT(m_psh.dwSize == sizeof(m_psh));
  1603.     ASSERT((m_psh.dwFlags & PSH_PROPSHEETPAGE) == PSH_PROPSHEETPAGE);
  1604. }
  1605.  
  1606. void CPropertySheetEx::Dump(CDumpContext& dc) const
  1607. {
  1608.     CWnd::Dump(dc);
  1609.  
  1610.     dc << "m_strCaption = " << m_strCaption << "\n";
  1611.     dc << "Number of Pages = " << m_pages.GetSize() << "\n";
  1612.     dc << "Stacked = " << m_bStacked << "\n";
  1613.     dc << "Modeless = " << m_bModeless << "\n";
  1614. }
  1615. #endif //_DEBUG
  1616. #endif // _WIN32_WCE
  1617.  
  1618. #ifdef AFX_INIT_SEG
  1619. #pragma code_seg(AFX_INIT_SEG)
  1620. #endif
  1621.  
  1622. IMPLEMENT_DYNAMIC(CPropertyPage, CDialog)
  1623. IMPLEMENT_DYNAMIC(CPropertySheet, CWnd)
  1624. WCE_DEL IMPLEMENT_DYNAMIC(CPropertyPageEx, CPropertyPage)
  1625. WCE_DEL IMPLEMENT_DYNAMIC(CPropertySheetEx, CPropertySheet)
  1626.  
  1627. /////////////////////////////////////////////////////////////////////////////
  1628.